/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */
#include "InputSystem.h"

namespace Fish3D
{
	InputSystem::InputSystem(void)
	{
	}

	InputSystem::~InputSystem(void)
	{
		//Shut down OIS
		inputManager->destroyInputObject(_keyboard);
		inputManager->destroyInputObject(_mouse);
		OIS::InputManager::destroyInputSystem(inputManager);
	}

	void InputSystem::init(Ogre::RenderWindow* win)
	{
		_win=win;
		if( inputManager==NULL ) {
			OIS::ParamList paramList;    
			size_t windowHnd = 0;
			std::ostringstream windowHndStr;

			// Get window handle
			_win->getCustomAttribute( "WINDOW", &windowHnd );

			// Fill parameter list
			windowHndStr << (unsigned int) windowHnd;
			paramList.insert( std::make_pair( std::string( "WINDOW" ), windowHndStr.str() ) );
			//paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
			//paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
			// Create input system
			inputManager = OIS::InputManager::createInputSystem( paramList );

			// Create a buffered _keyboard
			if( inputManager->getNumberOfDevices(OIS::OISKeyboard) > 0 ) {
				_keyboard = static_cast<OIS::Keyboard*>( inputManager->createInputObject( OIS::OISKeyboard, true ) );
				_keyboard->setEventCallback(this);
			}

			// Create a buffered _mouse
			if( inputManager->getNumberOfDevices(OIS::OISMouse) > 0 ) {
				_mouse = static_cast<OIS::Mouse*>( inputManager->createInputObject( OIS::OISMouse, true ) );
				_mouse->setEventCallback(this);

				// Get window size
				unsigned int width, height, depth;
				int left, top;
				_win->getMetrics( width, height, depth, left, top );

				// Set _mouse region
				this->setWindowExtents( width, height);
			}
		}
	}

	void InputSystem::capture()
	{
		if(_mouse){
			_mouse->capture();
		}

		if(_keyboard){
			_keyboard->capture();
		}
	}

	void InputSystem::addKeyListener(OIS::KeyListener *keyListener, const std::string& instanceName)
	{
		std::map<std::string, OIS::KeyListener*>::iterator it = keyListeners.find( instanceName );
        if( it == keyListeners.end() ) {
            keyListeners[ instanceName] = keyListener;
		}else{
			//add fail for name collision
		}
	}

	void InputSystem::addMouseListener( OIS::MouseListener *mouseListener, const std::string& instanceName)
	{
		std::map<std::string, OIS::MouseListener*>::iterator it = mouseListeners.find( instanceName );
        if( it == mouseListeners.end() ) {
            mouseListeners[ instanceName ] = mouseListener;
        }
	}

	void InputSystem::removeKeyListener( const std::string& instanceName)
	{
		std::map<std::string, OIS::KeyListener*>::iterator it = keyListeners.find( instanceName );
		if( it != keyListeners.end() ) {
			keyListeners.erase( it );
		}
	}
	void InputSystem::removeMouseListener( const std::string& instanceName)
	{
		std::map<std::string, OIS::MouseListener*>::iterator it = mouseListeners.find( instanceName );
		if( it != mouseListeners.end() ) {
			mouseListeners.erase( it );
		}
	}

	bool InputSystem::keyPressed( const OIS::KeyEvent &e)
	{
		std::map<std::string, OIS::KeyListener*>::iterator it = keyListeners.begin();
		std::map<std::string, OIS::KeyListener*>::iterator itEnd = keyListeners.end();
		for(; it != itEnd; ++it ) {
			if(!it->second->keyPressed( e ))
				break;
		}

		return true;
	}

	bool InputSystem::keyReleased( const OIS::KeyEvent &e)
	{
		std::map<std::string, OIS::KeyListener*>::iterator it = keyListeners.begin();
		std::map<std::string, OIS::KeyListener*>::iterator itEnd = keyListeners.end();
		for(; it != itEnd; ++it ) {
			if(!it->second->keyReleased( e ))
				break;
		}

		return true;
	}

	bool InputSystem::mouseMoved( const OIS::MouseEvent &e )
	{
		std::map<std::string, OIS::MouseListener*>::iterator it = mouseListeners.begin();
		std::map<std::string, OIS::MouseListener*>::iterator itEnd  = mouseListeners.end();
		for(; it != itEnd; ++it ) {
			if(!it->second->mouseMoved( e ))
				break;
		}

		return true;
	}
	bool InputSystem::mousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id )
	{
		std::map<std::string, OIS::MouseListener*>::iterator it = mouseListeners.begin();
		std::map<std::string, OIS::MouseListener*>::iterator itEnd  = mouseListeners.end();
		for(; it != itEnd; ++it ) {
			if(!it->second->mousePressed(e,id ))
				break;
		}

		return true;
	}
	bool InputSystem::mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id )
	{
		std::map<std::string, OIS::MouseListener*>::iterator it = mouseListeners.begin();
		std::map<std::string, OIS::MouseListener*>::iterator itEnd  = mouseListeners.end();
		for(; it != itEnd; ++it ) {
			if(!it->second->mouseReleased( e,id ))
				break;
		}

		return true;
	}

	void InputSystem::setWindowExtents(int width,int height)
	{
		const OIS::MouseState &mouseState = _mouse->getMouseState();
		mouseState.width  = width;
		mouseState.height = height;
	}
}
